home *** CD-ROM | disk | FTP | other *** search
/ Shareware Grab Bag / Shareware Grab Bag.iso / 050 / madtrb17.arc / LSPELL.PAS < prev    next >
Pascal/Delphi Source File  |  1985-12-29  |  25KB  |  940 lines

  1. {
  2.     An experimental Turbo Lightning-based document speller.
  3.  
  4.     This program is hereby placed in the public domain.  You may copy it,
  5.     modify it, and use it; you may NOT sell it or in any other way attempt
  6.     to make money from it.
  7.  
  8.     The author assumes no liability for any damage of any kind resulting
  9.     from use of this program.  All risk of use is on the user.  You are
  10.     warned that this program is experimental.
  11.  
  12.     Christopher J. Dunford
  13.     The Cove Software Group
  14.     10057-2 Windstream Drive
  15.     Columbia, Maryland 21044
  16.  
  17.     CompuServe 76703,2002
  18.  
  19.     12/28/85
  20.  
  21.     Turbo Lightning is a trademark of Borland International, Inc.
  22. }
  23.  
  24. Program Spell;
  25.  
  26. Const
  27.     Dummy: String[10] = '         ';
  28.     MaxLearn = 200;                       { Max # Learn table entries }
  29.  
  30. Type
  31.     Str66 = String[66];
  32.     Str128 = String[128];
  33.     Str255 = String [255];
  34.     S255Ptr = ^Str255;
  35.  
  36.     CharSet = Set of Char;
  37.  
  38.     TLPtrType = ^TLtype;
  39.  
  40.     TLtype = record
  41.         Rsrv1,                           { RSRVx is stuff we don't use }
  42.         Rsrv2: Integer;
  43.         Rsrv3,
  44.         Rsrv4,
  45.         Rsrv5: Array [0..2] of byte;
  46.         Rsrv6,
  47.         Rsrv7,
  48.         Rsrv8,
  49.         Rsrv9,
  50.         Rsrv10,
  51.         Rsrv11,
  52.         Rsrv12,
  53.         Rsrv13,
  54.         Rsrv14,
  55.         Rsrv15,
  56.         Rsrv16,
  57.         AuxFileOfs,             { Auxi Dict filename offset }
  58.         Rsrv17,
  59.         Rsrv18,
  60.         Rsrv19,
  61.         SubstList,              { Offset of substitute word list }
  62.         Rsrv20,
  63.         Rsrv21,
  64.         Rsrv22,
  65.         Rsrv23: Integer;
  66.     End;
  67.  
  68. Var
  69.     f,                      { Input file }
  70.     g: file of byte;        { Output file }
  71.     AuxName: Str66;         { Name of auxiliary dictionary file }
  72.     infile,                 { Name of input file }
  73.     bakfile: Str128;        { Derived name of backup file }
  74.  
  75.     TLPtr: TLPtrType;       { Ptr to Lightning's info structure }
  76.  
  77.     SkipWord,               { TRUE if user selected SKIP for current word }
  78.     Abort,                  { Cancel flag }
  79.     SaveAutoProof:          { User's autoproof status }
  80.             Boolean;
  81.  
  82.     WordCount,              { Word count }
  83.     InPtr,                  { Pointer to next char of input }
  84.     wstart,                 { Ptr to start of current word in input }
  85.     wlen,                   { Length of current word }
  86.     InLen,                  { Length of current input line }
  87.     Terminator,             { Char which terminated current input line }
  88.     LearnCount:             { Number of entried is Learn table }
  89.             Integer;
  90.  
  91.     LearnList:              { Table of Learn words }
  92.             Array[1..MaxLearn] Of String[32];
  93.  
  94.     w,                      { Word currently being checked }
  95.     InStr,                  { Current input line }
  96.     OutStr:                 { Current output line }
  97.             Str255;
  98.  
  99.     ch: char;               { A junk character }
  100.  
  101.  
  102. Function Lightning(fcode,
  103.                    alvalue,
  104.                    cxvalue,
  105.                    dxvalue: Integer;
  106.                    var AnyString): Integer;
  107.  
  108. {  This function calls Lightning and returns a status code  }
  109.  
  110. Type CPU = record case integer of
  111.             1: (AX, BX, CX, DX, BP, SI, DI, DS, ES, Flags: Integer);
  112.             2: (AL, AH, BL, BH, CL, CH, DL, DH: Byte);
  113.     End;
  114.  
  115. Var
  116.     R: CPU;
  117.  
  118. Begin
  119.     R.al := Lo (alvalue);
  120.     R.ah := $ed;
  121.     R.bh := $ed;
  122.     R.bl := Lo (fcode);
  123.     R.cx := cxvalue;
  124.     R.dx := dxvalue;
  125.     R.ds := seg (AnyString);
  126.     R.si := ofs (AnyString);
  127.     intr ($16,R);
  128.     Lightning := R.ax
  129. End;
  130.  
  131.  
  132. Function AutoProof(On: boolean): boolean;
  133. {  Set/reset AutoProof mode and return the previous state. }
  134. Var
  135.     NewState: integer;
  136. Begin
  137.     If On Then
  138.         NewState := $FF
  139.     Else
  140.         NewState := 0;
  141.     AutoProof := (Lightning(6, NewState, 0, 0, Dummy) <> 0)
  142. End;
  143.  
  144.  
  145. Function TLAddress: TLPtrType;
  146. {
  147.     Return a pointer to Lightning's internal data structure.
  148.     This structure is represented here by the TLtype.
  149. }
  150. Begin
  151.     TLAddress := Ptr(Lightning(2, 0, 0, 0, Dummy), Lightning(3, 0, 0, 0, Dummy));
  152. End;
  153.  
  154.  
  155. Function  LightningPresent : Boolean;
  156. { Return TRUE if Lightning is active }
  157. Begin
  158.     LightningPresent := (Lightning (0, 0, 0, 0, Dummy) = $5205)
  159. End;
  160.  
  161.  
  162. Function GetNumSubst: integer;
  163. {
  164.     Return number of words that are likely substitutes for
  165.     the last word that was checked.  The words themselves are
  166.     in a list of strings; the address of this list is obtained
  167.     by getting a pointer to the TL record (by calling function
  168.     TLAddress).  The segment of the list is the same as the
  169.     TL record's segment, and the offset is contained in the
  170.     record itself (field LikeyWordOfs).
  171.  
  172.     The list is a series of strings, each with a leading length byte.
  173. }
  174. Begin GetNumSubst := Lightning ($F, 0, 0, 0, Dummy) End;
  175.  
  176.  
  177. Procedure MakeAuxDictName;
  178. {
  179.     Access Lightning's data structure to access the current
  180.     Auxiliary dictionary's filename.   Assign the name to the
  181.     variable AuxName.
  182. }
  183. Var
  184.     AuxNamePtr: Record Case Boolean Of
  185.                     True:  (c: ^char);
  186.                     False: (O : integer;
  187.                             S : integer;);
  188.                 End;
  189.     i: Integer;
  190.  
  191. Begin
  192.     AuxNamePtr.S := Seg(TLPtr^);
  193.     AuxNamePtr.O := TLPtr^.AuxFileOfs;
  194.     i := 0;
  195.     While AuxNamePtr.c^ <> #0 Do Begin
  196.         i := Succ(i);
  197.         AuxName [i] := AuxNamePtr.c^;
  198.         AuxNamePtr.O := Succ(AuxNamePtr.O)
  199.     End;
  200.  
  201.     AuxName [0] := char(i);
  202.     While (i > 0) And (AuxName[i] <> '\') And (AuxName[i] <> '.') Do
  203.         i := Pred(i);
  204.     If (i = 0) Or ((i > 0) And (AuxName[i] = '\')) Then
  205.         AuxName := AuxName + '.DIC'
  206. End;
  207.  
  208.  
  209. Function LoadAuxDict: boolean;
  210. {
  211.     Force Lightning to load the auxiliary dictionary; return TRUE
  212.     if OK.  Note that it appears necessary to force a reload after
  213.     each word is added to the Auxi dic.
  214. }
  215. Begin
  216.     LoadAuxDict := (Lightning (4, 0, 0, 0, AuxName) = 0)
  217. End;
  218.  
  219.  
  220. Procedure Wait;
  221. { Display message and wait for a keystroke }
  222. Var ch: char;
  223. Begin
  224.      Write ('Strike any key to continue...');
  225.      Read (kbd, ch)
  226. End;
  227.  
  228.  
  229. Function tolower(ch:char): char;
  230. { Lowercase a character }
  231. Begin
  232.     If (ch In ['A'..'Z'])
  233.         Then tolower := chr(ord(ch) + 32)
  234.         Else tolower := ch
  235. End;
  236.  
  237.  
  238. Function toupper(ch:char): char;
  239. { Uppercase a character }
  240. Begin
  241.     If (ch In ['a'..'z'])
  242.         Then toupper := chr(ord(ch) - 32)
  243.         Else toupper := ch
  244. End;
  245.  
  246.  
  247. Function HasDigit: Boolean;
  248. { Return TRUE if the word 'w' contains a digit }
  249. Var
  250.     i: Integer;
  251.     Digit: Boolean;
  252. Begin
  253.     i := 1;  Digit := False;
  254.     While (i <= wlen) And Not Digit Do Begin
  255.         Digit := (w[i] In ['0'..'9']);
  256.         i := i+1
  257.     End;
  258.     HasDigit := Digit
  259. End;
  260.  
  261.  
  262. Procedure ReadLine;
  263. {
  264.     Read next line from input file.  Can't use simple ReadLn
  265.     because the input may be a word processor document, potentially
  266.     containing any number of weird characters (including x'00'
  267.     and ^Z, incidentally) and may not be terminated by a standard
  268.     CRLF pair.  We'll consider the line to be terminated by any
  269.     of the various CR/LF's.  The actual terminator will be placed
  270.     in the global var 'Terminator'; if there is no terminator (i.e.,
  271.     file read to EOF without a line end, Terminator will be set to -1.
  272.     On exit, the input line will be in the global var 'InStr', and its
  273.     length in 'InLen'.
  274. }
  275. Var
  276.     EOL: Boolean;
  277.     ch: Char;
  278.     q: byte;
  279.  
  280. Begin
  281.     InStr := '';
  282.     EOL := False;
  283.     InLen := 0;
  284.     Terminator := -1;
  285.  
  286.     While (InLen < 255) And Not Abort And Not EOL And Not EOF(f) Do Begin
  287.         {$i-} Read (f, q); {$i+}
  288.         If IOResult <> 0 Then Begin
  289.             ClrScr;
  290.             Gotoxy (1,10);
  291.             WriteLn ('Error reading input file...cancelling...');
  292.             Abort := TRUE;
  293.         End Else Begin
  294.             ch := chr(q);
  295.             EOL := ch In [chr($0D), chr($0A), chr($8D), chr($8A)];
  296.             If Not EOL Then Begin
  297.                 InLen := InLen+1;
  298.                 InStr[InLen] := ch
  299.             End Else
  300.                 Terminator := ord(ch)
  301.         End
  302.     End;
  303.  
  304.     InStr[0] := chr(InLen)
  305. End;
  306.  
  307.  
  308.  
  309. Procedure GetWord;
  310. {
  311.     Get the next "word" from the input stream and places
  312.     it in the global variable 'w'. Adds all leading separators
  313.     to 'OutStr'.  On exit, 'wstart' points to the start of
  314.     the word in InStr, and 'wlen' is the word's length.
  315. }
  316.  
  317. Var
  318.     OutLen: Integer;
  319.     Alphameric: Boolean;
  320.  
  321. Begin
  322.     wlen := 0;
  323.  
  324.     { Scan off leading non-alphanumerics }
  325.     OutLen := Length (OutStr);
  326.     Alphameric := False;
  327.     While Not Alphameric Do Begin
  328.         If InPtr > InLen Then
  329.             Alphameric := True
  330.         Else Begin
  331.             ch := InStr[InPtr];
  332.             Alphameric := (ch In ['a'..'z', 'A'..'Z', '0'..'9']);
  333.             If Not Alphameric Then Begin
  334.                 OutLen := OutLen + 1;
  335.                 OutStr[OutLen] := ch;
  336.                 InPtr := InPtr+1
  337.             End
  338.         End
  339.     End;
  340.     OutStr[0] := chr(OutLen);
  341.  
  342.     { Get word...until next non-alphanumeric }
  343.     If InPtr <= InLen Then Begin
  344.         wstart := InPtr;
  345.         While AlphaMeric Do Begin
  346.             If InPtr > InLen Then
  347.                 Alphameric := False
  348.             Else Begin
  349.                 ch := InStr[InPtr];
  350.                 Alphameric := (ch In ['a'..'z', 'A'..'Z', '0'..'9', '''']);
  351.                 If Alphameric Then Begin
  352.                     wlen := wlen + 1;
  353.                     w[wlen] := ch;
  354.                     InPtr := InPtr+1
  355.                 End
  356.             End
  357.         End
  358.     End;
  359.  
  360.     w[0] := chr(wlen);
  361.     If wlen <> 0 Then WordCount := WordCount + 1
  362. End;
  363.  
  364.  
  365. Procedure WriteLine;
  366. {
  367.     Write the line 'OutStr' to the output file.  Write the
  368.     'Terminator' character if it is not -1.
  369. }
  370. Var
  371.     i: Integer;
  372.     ch: Char;
  373.     q: Byte;
  374.  
  375.         Function CheckIO: Boolean;
  376.         Begin
  377.             If IOResult <> 0 Then Begin
  378.                 ClrScr; Gotoxy (1,10);
  379.                 WriteLn ('Error writing output file...cancelling...');
  380.                 Abort := True;
  381.                 CheckIO := TRUE
  382.             End Else
  383.                 CheckIO := FALSE
  384.         End;
  385.  
  386. Begin
  387.     For i := 1 To Length(OutStr) Do Begin
  388.         q := byte(OutStr[i]);
  389.         {$i-} Write (g, q); {$i+}
  390.         If CheckIO Then Exit;
  391.     End;
  392.  
  393.     If Terminator <> -1 Then Begin
  394.         q := byte (Terminator);
  395.         {$i-} Write (g, q); {$i+}
  396.         If CheckIO Then Exit;
  397.     End
  398. End;
  399.  
  400.  
  401.  
  402. Function WordInDict: Boolean;
  403. {
  404.     Look up the word 'w'.  Word is considered found (return value TRUE) if:
  405.         1.  w is a null string.
  406.         2.  w contains any digits
  407.         3.  w is in the list of Learn words.
  408.         4.  (Failing the above) Lightning can find the word.
  409.  
  410.     If the word is not found but is terminated with <'> or <'s> or
  411.     <'S>, then delete the possessive and look it up again.
  412.  
  413.     Note that Lightning checks the RAM dict, the Auxi dict, and the
  414.     disk dict in that order.
  415.  
  416. }
  417. Var
  418.     Found: Boolean;
  419.     APos, k: Integer;
  420.  
  421.         Function Lookup (wd: Str255): Boolean;
  422.         { Return TRUE if the word 'wd' is OK }
  423.         Var
  424.             Found: Boolean;
  425.             i: Integer;
  426.         Begin
  427.             If wd = '' Then
  428.                 Found := True
  429.             Else If HasDigit Then
  430.                 Found := True
  431.             Else Begin
  432.                 i := 1;  Found := False;
  433.                 While (i <= LearnCount) And Not Found Do Begin
  434.                     Found := (wd = LearnList[i]);
  435.                     i := i+1
  436.                 End;
  437.  
  438.                 If Not Found Then
  439.                     If (Lightning ($E, 0, 0, 0, wd) <> 1)
  440.                         Then Found := true
  441.                         Else Found := (Lightning (1, 0, 0, 0, wd) = 0)
  442.             End;
  443.             Lookup := Found
  444.         End;
  445.  
  446. Begin  { WordInDict }
  447.     Found := Lookup (w);
  448.     If Not Found Then Begin { Check for possessives }
  449.         APos := Pos('''', w);
  450.         If Apos <> 0 Then Begin
  451.             k := Length (w);
  452.             If (APos = k) Or ((Apos = k-1) And (toupper (w[k]) = 'S'))
  453.                 Then Found := Lookup (Copy (w, 1, APos-1))
  454.         End
  455.     End;
  456.     WordInDict := Found
  457. End;
  458.  
  459.  
  460. Procedure Phonetic;
  461. {
  462.     Drives the Phonetic (lookup) option.  Looks up possible
  463.     words, displays them, and gets a selection.  The selected
  464.     word is return in global var 'w'; if no selection is made,
  465.     'w' is unchanged.
  466. }
  467. Var
  468.     NumSubst, len, i, k, x, y: Integer;
  469.     Column, Columns, width, Long: Integer;
  470.     OK: Boolean;
  471.     ch: Char;
  472.     SubstPtr: Record Case Boolean Of
  473.                 True:  (SP: S255Ptr);
  474.                 False: (O: Integer;
  475.                         S: Integer;);
  476.             End;
  477.     s: Str255;
  478.  
  479. Begin
  480.     NumSubst := GetNumSubst; { Number of soundalike words }
  481.  
  482.     If NumSubst = 0 Then Begin
  483.         Write ('No phonetics found...strike any key...');
  484.         Read (kbd, ch);
  485.     End Else Begin
  486.         { Find longest likely }
  487.         SubstPtr.S := Seg(TLPtr^);
  488.         SubstPtr.O := TLPtr^.SubstList;
  489.         Long := 0;
  490.         For i := 1 To NumSubst Do Begin
  491.             Len := byte(SubstPtr.SP^[0]);
  492.             If len > Long Then Long := Len;
  493.             SubstPtr.O := SubstPtr.O + Len + 1;
  494.         End;
  495.  
  496.         { Calculate width, and number per line }
  497.         Long := Long + 6;
  498.         Columns := 79 DIV (Long);
  499.         Width := 79 DIV Columns;
  500.  
  501.         { Display word list }
  502.         Gotoxy (1,5);  ClrEOL;
  503.         Column := 0;
  504.  
  505.         SubstPtr.S := Seg(TLPtr^);
  506.         SubstPtr.O := TLPtr^.SubstList;
  507.  
  508.         For i := 1 To NumSubst Do Begin
  509.             len := byte(SubstPtr.SP^[0]);
  510.             s[0] := char(len);
  511.             move (SubstPtr.SP^[1], s[1], len);
  512.             Gotoxy (Column * width, WhereY);
  513.             If Column = 0 Then ClrEOL;
  514.             Write (i:2, ': ', s);
  515.             Column := Column + 1;
  516.             If Column = Columns Then Begin
  517.                 WriteLn;
  518.                 Column := 0;
  519.             End;
  520.             SubstPtr.O := SubstPtr.O + len + 1;
  521.         End;
  522.  
  523.         { Get selection }
  524.         WriteLn;
  525.         x := WhereX; y := WhereY;
  526.         Repeat
  527.             Gotoxy (x, y);  ClrEol;
  528.             Write ('Select number, or <Return>: ');
  529.             ReadLn (s);
  530.             If s = '' Then
  531.                 OK := True
  532.             Else If length(s) > 3 Then
  533.                 OK := False
  534.             Else Begin
  535.                 k := 0;
  536.                 OK := True;
  537.                 For i := 1 To Length(s) Do
  538.                     If (s[i] In ['0'..'9'])
  539.                         Then k := 10*k + ord(s[i]) - ord('0')
  540.                         Else OK := False;
  541.                 If OK Then OK := (k > 0) And (k <= NumSubst)
  542.             End
  543.         Until OK;
  544.  
  545.         { Get the selected word from the Lightning list }
  546.         If s <> '' Then Begin
  547.             SubstPtr.S := Seg(TLPtr^);
  548.             SubstPtr.O := TLPtr^.SubstList;
  549.             s[0] := char(36);
  550.  
  551.             For i := 1 To k-1 Do
  552.                 SubstPtr.O := SubstPtr.O +
  553.                              Succ(byte(SubstPtr.SP^[0]));
  554.             move (SubstPtr.SP^[1], s[1], byte(SubstPtr.SP^[0]));
  555.             s[0] := char(36);
  556.             w := copy(s, 1, byte(SubstPtr.SP^[0]))
  557.         End
  558.     End
  559. End;
  560.  
  561.  
  562. Procedure AddToAuxi;
  563. {
  564.     Add the word 'w' to the current auxiliary dictionary.  The Auxi
  565.     dict is just an ASCII text file contain a list of words, one
  566.     per line.  This procedure includes the option list for capitalization.
  567. }
  568. Var
  569.     Auxi: Text;
  570.     i, IOError: Integer;
  571.     dummy: Boolean;
  572.     Option: Char;
  573.     w1: Str255;
  574.  
  575.  
  576.     Function AddMenu: Char;
  577.     { Return a selection from the ADD Option menu }
  578.     Var ch: Char;
  579.     Begin
  580.         Write ('Add option: A(s shown  U(ppercase  L(owercase  I(nitial  <Esc> ');
  581.         Repeat
  582.             Read (kbd, ch);
  583.             ch := toupper (ch);
  584.         Until (ch In ['A', 'U', 'L', 'I', chr(27)]);
  585.         AddMenu := ch
  586.     End;
  587.  
  588.  
  589.     Function CheckIO: Boolean;
  590.     {
  591.         If current IOResult is nonzero, display a message and close
  592.         the Auxi file.  Return TRUE if there was an error.
  593.     }
  594.     Begin
  595.         If IOResult <> 0 Then Begin
  596.             WriteLn;
  597.             WriteLn ('Error updating aux dict file ', AuxName);
  598.             Wait;
  599.             close (Auxi);
  600.             CheckIO := TRUE
  601.         End Else
  602.             CheckIO := FALSE
  603.     End;
  604.  
  605. Begin { AddToAuxi }
  606.     Option := AddMenu;
  607.     Gotoxy (1, WhereY); ClrEOL;
  608.     If Option <> chr(27) Then Begin
  609.         w1 := w;
  610.         Case Option Of { Handle the capitalization option }
  611.         'U': For i := 1 To Length (w1) Do w1[i] := toupper (w1[i]);
  612.         'L': For i := 1 To Length (w1) Do w1[i] := tolower (w1[i]);
  613.         'I': Begin
  614.                 w1[1] := toupper (w1[1]);
  615.                 For i := 2 To Length(w1) Do w1[i] := tolower (w1[i])
  616.              End
  617.         End;
  618.  
  619.         {$i-}
  620.         Repeat
  621.             If AuxName = '' Then
  622.                 IOError := -1
  623.             Else Begin
  624.                 Assign (Auxi, AuxName);
  625.                 IOError := IOResult;
  626.             End;
  627.  
  628.             If IOError = 0 Then Begin
  629.                 Append (Auxi);
  630.                 IOError := IOResult;
  631.             End;
  632.  
  633.             If IOError <> 0 Then Begin
  634.                 ClrScr;  Gotoxy (1,10);
  635.                 WriteLn ('Unable to open auxiliary dictionary file ', AuxName);
  636.                 Write ('Enter new aux dict name, or <Return> for none: ');
  637.                 ReadLn (AuxName);
  638.                 Gotoxy (1,10); ClrEOL;
  639.                 Gotoxy (1,11); ClrEOL;
  640.                 If AuxName = '' Then Exit;
  641.             End;
  642.         Until IOError = 0;
  643.  
  644.         Writeln (Auxi, w1);
  645.         If CheckIO Then Exit;
  646.  
  647.         Write (Auxi, chr(26));
  648.         If CheckIO Then Exit;
  649.  
  650.         Close (Auxi);
  651.         {$i+}
  652.  
  653.         SkipWord := True;     { In case capitalization in Auxi is different }
  654.         dummy := LoadAuxDict  { Force reload }
  655.     End
  656. End;
  657.  
  658.  
  659.  
  660. Procedure CorrectError;
  661. {
  662.     Drives the stuff that happens when a word isn't found:
  663.         Display the misspelled word in context.
  664.         Get a correction option selection.
  665.         Case Option of
  666.             Edit:     get a new word from keyboard
  667.             Skip:     set SkipWord to TRUE
  668.             Learn:    add word to Learn list
  669.             Phonetic: perform Phonetic procedure
  670.             Add:      perform AddToAuxi procedure
  671.             Cancel:   set Abort to TRUE
  672.  
  673.     On exit, the corrected word is in global var 'w'.  THIS WORD
  674.     SHOULD BE RECHECKED!!!  I.e., for each word 'w', the checker
  675.     should loop until:
  676.         1. Abort is TRUE, or
  677.         2. Skip is TRUE, or
  678.         3. word is verified by the WordInDict procedure
  679. }
  680.  
  681.     Procedure HiliteWord;
  682.     {
  683.         Display the misspelled word 'w' in context at top of screen.
  684.     }
  685.     Begin
  686.         ClrScr; Gotoxy (1,2);
  687.         Write (OutStr);
  688.  
  689.         TextColor (0); TextBackground (7);
  690.         Write (w);
  691.         TextColor (7); TextBackground (0);
  692.  
  693.         If InPtr <= InLen Then
  694.             Write (Copy (InStr, Inptr, Length(InStr)-Inptr));
  695.  
  696.         WriteLn;
  697.     End;
  698.  
  699.  
  700.     Function OptionMenu: Char;
  701.     { Get the user option from the misspelled word menu }
  702.     Var
  703.         i: Integer;
  704.         ch: Char;
  705.     Begin
  706.         Gotoxy (1, 4);
  707.         For i := 1 To 80 Do Write ('-'); WriteLn;
  708.  
  709.         Gotoxy (1, 6);
  710.         Write ('Select: S(kip  L(earn  E(dit  P(honetic  A(dd to dict  C(ancel ');
  711.  
  712.         Repeat
  713.             Read (kbd, ch);
  714.             ch := toupper (ch)
  715.         Until (ch In ['S', 'L', 'E', 'P', 'A', 'C']);
  716.         WriteLn (ch);
  717.         OptionMenu := ch
  718.     End;
  719.  
  720.  
  721.     Procedure EditWord;
  722.     { Get a replacement word from keyboard and put it in 'w' }
  723.     Begin
  724.         WriteLn;
  725.         Write ('Enter correction, or <Return> to delete word: ');
  726.         ReadLn (w)
  727.     End;
  728.  
  729.  
  730.     Procedure AddToLearns;
  731.     { Add word 'w' to the Learn word list }
  732.     Begin
  733.         If LearnCount < MaxLearn Then Begin
  734.             LearnCount := LearnCount + 1;
  735.             LearnList[LearnCount] := w
  736.         End
  737.     End;
  738.  
  739. Begin { CorrectError }
  740.     HiliteWord;
  741.     Case OptionMenu Of
  742.          'S': SkipWord := True;
  743.          'L': AddToLearns;
  744.          'E': EditWord;
  745.          'P': Phonetic;
  746.          'A': AddToAuxi;
  747.          'C': Abort := TRUE
  748.      End;
  749.      Gotoxy (1,1); ClrEOL;
  750. End;
  751.  
  752.  
  753.  
  754. Procedure Init;
  755. {
  756.     Program initialization
  757. }
  758.  
  759.  
  760.     Procedure Logo;
  761.     Begin
  762.         WriteLn ('lspell 0.93 Copyright (c) 1985 Cove Software Group');
  763.     End;
  764.  
  765.  
  766.     Procedure Usage;
  767.     Begin
  768.         Logo;
  769.         WriteLn ('usage:- lspell filename');
  770.         Halt;
  771.     End;
  772.  
  773.  
  774.     Function Exist (s: Str128): Boolean;
  775.     { Returns TRUE if file 's' exists }
  776.     Var f: File;
  777.     Begin
  778.         {$i-}
  779.         Assign (f, s);
  780.         Reset (f);
  781.         Exist := (IOResult = 0);
  782.         Close (f);
  783.         {$i-}
  784.     End;
  785.  
  786.  
  787. Begin { Init }
  788.     { Get rid of Turbo's stupid hi-intensity video }
  789.     TextColor (7);  TextBackground (0);
  790.  
  791.     { Ensure Lightning is running }
  792.     If Not LightningPresent Then Begin
  793.         Logo;
  794.         WriteLn ('lspell: Turbo Lightning (tm) not present');
  795.         Halt;
  796.     End;
  797.  
  798.     { Make sure we got right # of parms }
  799.     If ParamCount <> 1 Then Usage;
  800.  
  801.     { Derive the backup file name }
  802.     infile := ParamStr (1);
  803.     If pos('.', infile) > 0 Then Begin
  804.         bakfile := copy(infile, 1, pos('.', infile)-1);
  805.         bakfile := concat (bakfile, '.@ls');
  806.     End Else
  807.         bakfile := concat (infile, '.@ls');
  808.  
  809.     { Make sure we haven't been asked to spell a .@ls file }
  810.     If infile = bakfile Then Begin
  811.         Logo;
  812.         WriteLn ('lspell: can''t spellcheck a .@ls file');
  813.         Halt;
  814.     End;
  815.  
  816.     { Check for input file existence }
  817.     If Not exist (infile) Then Begin
  818.         WriteLn ('lspell: can''t find input file ', infile);
  819.         Halt
  820.     End;
  821.  
  822.     {$i-}
  823.  
  824.     { Erase old .@ls file }
  825.     If Exist(bakfile) Then Begin
  826.         Assign (f, bakfile);
  827.         Erase (f);
  828.     End;
  829.  
  830.     { Rename the input file to .@ls }
  831.     Assign (f, infile);
  832.     Rename (f, bakfile);
  833.  
  834.     { Open input (now .@ls) }
  835.     Assign (f, bakfile);
  836.     reset (f);
  837.     If IOResult <> 0 Then Begin
  838.         Logo;
  839.         WriteLn ('lspell: can''t find input file', infile);
  840.         Halt;
  841.     End;
  842.  
  843.     { Create output file (using original filename) }
  844.     Assign (g, infile);
  845.     Rewrite (g);
  846.     If IOResult <> 0 Then Begin
  847.         Logo;
  848.         WriteLn ('lspell: error opening output file');
  849.         Halt;
  850.     End;
  851.     {$i+}
  852.  
  853.     { Set up the pointer to the Lightning info structure }
  854.     TLPtr := TLAddress;
  855.  
  856.     { Get the name of the auxiliary dictionary, and load it }
  857.     MakeAuxDictName;
  858.     If Not LoadAuxDict Then Begin
  859.         Logo;
  860.         WriteLn ('Warning: auxiliary dictionary ', AuxName, ' not found');
  861.         Wait
  862.     End;
  863.  
  864.     { Turn off autoproof and save user's status }
  865.     SaveAutoProof := AutoProof(False);
  866.  
  867.     { Initialize a couple of variables and prepare the screen }
  868.     Abort := FALSE;
  869.     LearnCount := 0;
  870.     WordCount := 0;
  871.     ClrScr;
  872.     Gotoxy (1,2)
  873. End; { Init }
  874.  
  875.  
  876. Procedure CheckSpelling;
  877. {
  878.     Main spelling loop.  Reads input line-by-line, gets
  879.     words and checks them, and writes output.
  880. }
  881. Begin
  882.     While Not Abort And Not EOF(f) Do Begin
  883.         ReadLine;
  884.         If Not Abort Then Begin
  885.             Gotoxy (1,2);  ClrEOL;  Write (Copy (InStr, 1, 79));
  886.             OutStr := '';
  887.  
  888.             InPtr := 1;
  889.             While InPtr <= InLen Do Begin
  890.                 GetWord;
  891.                 SkipWord := False;
  892.                 While Not Abort And Not SkipWord And Not WordInDict Do
  893.                     CorrectError;
  894.                 OutStr := concat (OutStr, w)
  895.             End;
  896.  
  897.             WriteLine
  898.         End
  899.     End
  900. End;
  901.  
  902.  
  903. Procedure Terminate;
  904. {
  905.     Termination.  Fix up the files, and restore autoproof.
  906. }
  907. Var
  908.     dummy: Boolean;
  909. Begin
  910.     Close (f);
  911.     Close (g);
  912.  
  913.     ClrScr;
  914.  
  915.     { If program aborted, get rid of the partial output file
  916.       and rename the backup file to its old name
  917.     }
  918.     If Abort Then Begin
  919.         {$i-}
  920.         Assign (f, infile);  { Erase the aborted output file }
  921.         Erase (f);
  922.         Assign (f, bakfile); { Restore .@ls to original name }
  923.         Rename (f, infile)
  924.         {$i+}
  925.     End Else Begin
  926.         Gotoxy (1,10);
  927.         Write ('Spelling complete: ', WordCount, ' words')
  928.     End;
  929.  
  930.     { Restore user's autoproof status }
  931.     dummy := AutoProof(SaveAutoProof)
  932. End;
  933.  
  934.  
  935. Begin { Spell }
  936.     Init;
  937.     CheckSpelling;
  938.     Terminate
  939. End.
  940.